package com.heima.wemedia.service.impl;

import com.heima.common.aliyun.GreenImageUploadScan;
import com.heima.common.aliyun.GreenImageUrlScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.delayTask.RedisDelayedQueueUtil;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.common.util.JsonUtils;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.feign.admin.AdminClient;
import com.heima.feign.article.ArticleFilent;
import com.heima.file.service.MinioService;
import com.heima.model.admin.dtos.ChannelNameDto;
import com.heima.model.media.dtos.WmNewsContentDto;
import com.heima.model.media.dtos.WmNewsDto;
import com.heima.model.media.dtos.WmNewsResultDTO;
import com.heima.model.media.dtos.WmNewsStudateEnmu;
import com.heima.wemedia.entity.WmNews;
import com.heima.wemedia.entity.WmSensitive;
import com.heima.wemedia.service.WmAuditService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmSensitiveService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmAuditServiceImpl implements WmAuditService , InitializingBean {

    @Autowired
    private WmNewsService wmNewsService;

    @Autowired
    private ArticleFilent articleFilent;

    @Autowired
    private GreenTextScan textScan;

    @Autowired
    private GreenImageUploadScan imageUploadScan;

    @Autowired
    private WmSensitiveService wmSensitiveService;


    @Autowired
    private RedisDelayedQueueUtil redisDelayedQueueUtil;


    private static final String ARTILE_PUBLISH_DELAY_QUEUE = "article_delay_queue";

    /**
     * 自动审核
     * @param userId
     * @param dto
     *
     * 借助第三方提供的接口审核文本
     * 借助第三方提供的接口审核图片，将存储在minIO中的图片，先下载然后上传到阿里云进行审核
     * 如果审核不通过，则需要修改自媒体文章的状态，status:2  审核不通过    status:3  转到人工审核
     */
    @Override
    @Async("taskExecutor")
    public void auditAsync(Integer userId, WmNewsDto dto) {
           //自媒体发布文章  开始审核
        log.info("异步执自媒体文章审核");
        //  获取文本  图片
        String context = buildContext(dto.getContent(),dto.getTitle(),dto.getLabels());
       List<byte[]> imageList =  buildImagesByList(dto);
        boolean ret;
        //审核文本
        /* ret = aliyunTextAudit(context,dto);
        if(!ret){
            return;
        }*/
        //审核图片
      /*  ret = aliyunImagesAudit(imageList,dto);
        if(!ret){
            return;
        }*/

        //审核文本敏感词
       /* ret = dfaSensitiveWordsAudit(context,dto);
        if(!ret){
            return;
        }*/

        //审核图片敏感词
        // 图片敏感词审核
        /*ret = ocrSensitiveWordsAudit(imageList, dto);
        if(!ret){
            return;
        }*/

        //如果审核成功，则需要远程调用article服务创建app端需要的文章
        //修改状态
        updateStatus(WmNewsStudateEnmu.YIFABU.getCode(), null,dto.getId(),"审核通过");
        log.info("审核通过  调用feign接口");
        if (dto.getPublishTime() != null && dto.getPublishTime().getTime() > System.currentTimeMillis()){
            WmNewsResultDTO resultDTO = bulidWmNewsResult(userId, dto);
            String json = JsonUtils.toString(resultDTO);
            redisDelayedQueueUtil.addQueue(json,
                    dto.getPublishTime().getTime()-System.currentTimeMillis(),
                    TimeUnit.MILLISECONDS,
                    ARTILE_PUBLISH_DELAY_QUEUE);

        }else {
            //立即发布
            log.info("远程调用article接口创建article");
            WmNewsResultDTO resultDTO = bulidWmNewsResult(userId, dto);
            Long articleId = articleFilent.createArticle(resultDTO);
            this.updateStatus(WmNewsStudateEnmu.YIFABU.getCode(), articleId, dto.getId(), "");
            log.info("修改成已发布");
        }
    }

    @Autowired
    private Tess4jClient tess4jClient;

    private boolean ocrSensitiveWordsAudit(List<byte[]> imageList, WmNewsDto dto) {

        try{
            for(byte[] image : imageList){
                BufferedImage bufferImage = ImageIO.read(new ByteArrayInputStream(image));
                // 图片的文字内容
                String content = tess4jClient.doOCR(bufferImage);
                // 敏感词审核
                return dfaSensitiveWordsAudit(content, dto);
            }
            return true;
        }catch(Exception e){
            e.printStackTrace();
            log.error("图片敏感词审核审核异常：{}", e.getMessage());
            this.updateStatus(2, null, dto.getId(), e.getMessage());
            return false;
        }
    }

    private boolean dfaSensitiveWordsAudit(String context, WmNewsDto dto) {
        Collection<String> words = buildSensitiveWords();
        Map<String, Integer> map = SensitiveWordUtil.matchWords(words, context);
        if (CollectionUtils.isEmpty(map)){
            return true;
        }
        String collect = map.keySet().stream().collect(Collectors.joining(";"));
        this.updateStatus(WmNewsStudateEnmu.SHENHESHIBAI.getCode(), null, dto.getId(), collect);
        return false;


    }

    private Collection<String> buildSensitiveWords() {
        List<WmSensitive> list = wmSensitiveService.list();
        if(CollectionUtils.isEmpty(list)){
            return null;
        }
        return list.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());

    }

    private boolean aliyunImagesAudit(List<byte[]> imageList, WmNewsDto dto) {
        log.info("阿里云图片审核");
        try {
            Map<String, String> map = imageUploadScan.imageScan(imageList);
            return parseResult(map,dto.getId());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("阿里云图片审核异常：{}", e.getMessage());
            this.updateStatus(WmNewsStudateEnmu.SHENHESHIBAI.getCode(), null, dto.getId(), e.getMessage());
            return false;
        }


    }


    private boolean aliyunTextAudit(String context, WmNewsDto dto) {
        log.info("阿里云文本审核");
        try {
            Map<String, String> map = textScan.greenTextScan(context);
           return parseResult(map,dto.getId());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("阿里云文本审核异常：{}", e.getMessage());
            this.updateStatus(WmNewsStudateEnmu.SHENHESHIBAI.getCode(), null, dto.getId(), e.getMessage());
            return false;
        }

    }

    private boolean parseResult(Map<String, String> map, Integer wmNewsId) {
        String suggestion = map.get("suggestion");
        String reson = map.get("reson");
        if("pass".equals(suggestion)){
            return true;
        }
        if("block".equals(suggestion)){
            this.updateStatus(WmNewsStudateEnmu.SHENHESHIBAI.getCode(), null, wmNewsId, reson);
            return false;
        }
        if("review".equals(suggestion)){
            this.updateStatus(WmNewsStudateEnmu.RENGONGSHENHE.getCode(), null,wmNewsId, reson);
            return false;
        }
        return true;


    }

    @Autowired
    private MinioService minioService;

    private List<byte[]> buildImagesByList(WmNewsDto dto) {

        List<byte[]> result = new ArrayList<>();
        //context
        List<WmNewsContentDto> images = WmNewsContentDto.getImages(dto.getContent());
        if(CollectionUtils.isEmpty(images)){
            return null;
        }
        for (WmNewsContentDto image : images) {
            byte[] bytes = minioService.downLoadFile(image.getValue());
            if (bytes != null){
                result.add(bytes);
            }
        }
        // cover
        List<String> dtoImages = dto.getImages();
        if (!StringUtils.isEmpty(dtoImages)){
            for (String dtoImage : dtoImages) {
                byte[] bytes = minioService.downLoadFile(dtoImage);
                if (bytes != null){
                    result.add(bytes);
                }
            }
        }
        return result;
    }

    private String buildContext(String content, String title, String labels) {
        StringBuffer contextBuffer = new StringBuffer();
        List<WmNewsContentDto> text = WmNewsContentDto.getText(content);
        if (!CollectionUtils.isEmpty(text)){
            contextBuffer.append(text.stream().map(WmNewsContentDto::getValue).collect(Collectors.joining("-")));

        }
        contextBuffer.append(title).append("-");
        contextBuffer.append(labels);
        return contextBuffer.toString();


    }

    @Autowired
    private AdminClient adminClient;
    private WmNewsResultDTO bulidWmNewsResult(Integer userId,WmNewsDto dto) {
        WmNewsResultDTO result = new WmNewsResultDTO();
        BeanUtils.copyProperties(dto,result);
        result.setWmUserId(userId);
        ChannelNameDto channelNameDto = adminClient.findOne(dto.getChannelId());
        result.setChannelName(channelNameDto.getChannelName());
        result.setCreatedTime(new Date());
        if (CollectionUtils.isEmpty(dto.getImages())){
            result.setImages(dto.getImages().stream().collect(Collectors.joining(",")));

        }
        return result;
    }

    private void updateStatus(Integer status,Long articleId,Integer wmNewsId,String reason) {
        WmNews wmNews = new WmNews();
        wmNews.setStatus(status);
        wmNews.setId(wmNewsId);
        wmNews.setArticleId(articleId);
        wmNews.setReason(reason);
        wmNewsService.updateById(wmNews);
    }

    private ExecutorService taskMsgService = Executors.newSingleThreadExecutor();

    private ExecutorService businessService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors()*2,
            120L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1000),
            new ThreadPoolExecutor.CallerRunsPolicy());


    /**
     * 框架启动完成以后，启动死循环，去redisson的延迟队列中取任务执行
     * */
    @Override
    public void afterPropertiesSet(){
        taskMsgService.execute(new Runnable() {
            @Override
            public void run() {

                try {
                    while (true){

                        String value = redisDelayedQueueUtil.getDelayQueue(ARTILE_PUBLISH_DELAY_QUEUE);
                        // 业务逻辑处理
                        log.info("收到延迟任务： value={}", value);
                        businessService.execute(new Runnable() {
                            @Override
                            public void run() {
                                afterTaskMsg(value);
                            }
                        });
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        });
    }

    private void afterTaskMsg(String json){

        if(StringUtils.isEmpty(json)){
            return;
        }
        WmNewsResultDTO resultDTO = JsonUtils.toBean(json, WmNewsResultDTO.class);
        Long articleId = articleFilent.createArticle(resultDTO);
        // 更新wmNews的数据库的状态
        this.updateStatus(9, articleId, resultDTO.getId(), "");

    }


}
